//-------------------------------------------------------------------
//	Copyright (c) 2012-2014 Zhirnov Andrey
//	This file is part of the "UXGEN-ES" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------


#if not defined(ENGINE_META_DATA) and \
	not defined(EXTERNAL_META_DATA)
#	error target of meta data must be defined
#endif

#ifdef ENGINE_META_DATA
#	if not defined(__SYS_META_SERIALIZE) or \
	   not defined(__SYS_COPY_META_FIELDS) or \
	   not defined(__SYS_META_DESERIALIZE) or \
	   not defined(__SYS_META_DECL_FIELDS)
#		error macrosses must be defined
#	endif
#endif

#ifdef EXTERNAL_META_DATA
	using namespace UX_STL;

#	if not defined(__SYS_INIT_META_OBJECT) or \
	   not defined(__SYS_META_DECL_FIELDS) or \
	   not defined(__SYS_META_SERIALIZE) or \
	   not defined(__SYS_INIT_META_DEPENDANCY) or \
	   not defined(__SYS_INIT_FIELDS) or \
	   not defined(META_OBJECT_NAME)
#		error macrosses must be defined
#	endif
#endif

#if	not defined(META_FIELDS) or \
	not defined(META_OBJECT)
#	error macrosses must be defined
#endif


namespace MetaData
{

	//
	// Meta Data Object
	//

	struct META_OBJECT
#		ifdef META_OBJECT_PARENT
			: META_OBJECT_PARENT
#		endif
	{
	// types
	public:
		typedef META_OBJECT				Self;
		
#	if defined(ENGINE_META_DATA) and defined(META_BASECLASS)
		typedef META_BASECLASS			object_t;
#	endif
#	ifdef META_OBJECT_PARENT
		typedef META_OBJECT_PARENT		parent_t;
#	endif


	// variables
	public:
		META_FIELDS( __SYS_META_DECL_FIELDS );


	// methods
	public:
		META_OBJECT ()
		{
#	ifdef EXTERNAL_META_DATA
			META_FIELDS( __SYS_INIT_FIELDS );
#	endif
#	ifdef META_OBJECT_PARENT
			META_OBJECT_PARENT::_IsMetaDataObject();
#	endif
		}


		// read config form file
#	if defined( EXTERNAL_META_DATA ) and defined( META_NAME )

		static CMetaClass &	META_NAME ()
		{
			static CMetaClass	metaClass;
			return metaClass;
		}

		static CMetaClass & GetMetaClass ()
		{
			return META_NAME();
		}
		
		friend UX_CREATE_META_OBJECT( META_OBJECT,  META_NAME() );

		void InitMetaData () const
		{
			if ( META_NAME().IsInit() )
				return;

			META_FIELDS( __SYS_INIT_META_DEPENDANCY );

			CMetaAddIteratorPtr iter;
			
			META_NAME().CreateExt( StringUtils::ToMetaTypeName( META_OBJECT_NAME ).cstr(), *this );

#		ifdef META_OBJECT_PARENT
			META_OBJECT_PARENT::InitMetaData();
			META_NAME().Inherit( META_OBJECT_PARENT::GetMetaClass() );
#		endif

			iter = META_NAME().GetAddIter( *this );

			META_FIELDS( __SYS_INIT_META_OBJECT );
		}

		virtual CMetaSetIterator *	GetMetaIterator () const
		{
			return META_NAME().GetSetIter( *this );
		}
#	endif

#	ifdef ENGINE_META_DATA
		// read binary data form file
		virtual bool Deserialize (const RFilePtr &file)
		{
			FILE_RW_BEGIN();

#		ifdef META_OBJECT_PARENT
			FILE_RW_CHECK( META_OBJECT_PARENT::Deserialize( file ) );
#		endif
			META_FIELDS( __SYS_META_DESERIALIZE );

			FILE_RW_RETURN();
		}
#	endif

#	if defined(ENGINE_META_DATA) and defined(META_BASECLASS)
		virtual void Init (META_BASECLASS *obj, const EngineSubSystemsRef ess)
		{
#		ifdef META_OBJECT_PARENT
			META_OBJECT_PARENT::Init( obj, ess );
#		endif
			META_FIELDS( __SYS_COPY_META_FIELDS );
		}
#	endif

		// write binary data to file
		virtual bool Serialize (const WFilePtr &file) const
		{
			FILE_RW_BEGIN();

#		ifdef META_OBJECT_PARENT
			FILE_RW_CHECK( META_OBJECT_PARENT::Serialize( file ) );
#		endif
			META_FIELDS( __SYS_META_SERIALIZE );
			
			FILE_RW_RETURN();
		}

		// type name of object
		static const uni_c_string GetTypeNameStatic ()
		{
			static static_string<64> name = META_OBJECT_NAME;
			return name;
		}
		
		// type name of object
		virtual const uni_c_string GetTypeName () const
		{
			return GetTypeNameStatic();
		}

		// call function for all fields
		virtual void ForAllFields (void (*Func)(TypeId_t typeId, void *field, void *param), void *param)
		{
#		ifdef META_OBJECT_PARENT
			META_OBJECT_PARENT::ForAllFields( Func, param );
#		endif
			META_FIELDS( __SYS_META_FOR_ALL_FIELDS );
		}

	protected:
		// check if this type is meta data object
		static void _IsMetaDataObject () {}
	};


#	if defined(ENGINE_META_DATA) and defined(META_BASECLASS)
		template <>
		inline void MetaDataUtils::Copy <> (META_BASECLASS &left, META_OBJECT &right, const EngineSubSystemsRef ess)
		{
			right.Init( &left, ess );
		}
#	endif
		
#	if defined( EXTERNAL_META_DATA ) and defined( META_NAME )
		template <>
		inline void MetaDataUtils::InitMetaData <> (const META_OBJECT &obj)
		{
			obj.InitMetaData();
		}
		
		template <>
		inline void MetaDataUtils::InitMetaData <> (const array<META_OBJECT> &obj)
		{
			META_OBJECT().InitMetaData();
		}
#	endif
		

	template <>
	inline bool MetaDataUtils::Save (const WFilePtr &file, const META_OBJECT &value)
	{
		return value.Serialize( file );
	}
		
#	ifdef ENGINE_META_DATA
		template <>
		inline bool MetaDataUtils::Load (const RFilePtr &file, META_OBJECT &value)
		{
			return value.Deserialize( file );
		}
#	endif

}	// MetaData

	
#undef META_FIELDS
#undef META_OBJECT
#undef META_NAME
#undef META_BASECLASS
#undef META_OBJECT_NAME
#undef META_OBJECT_PARENT